#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "contexto.h"
#include "lista.h"
#include "abb.h"

struct palabra{
	char* valor;
	unsigned short frecuencia;
};
struct contenedorContextos{
	abb_t* contextos;
	unsigned short cantidad;
};
struct contexto{
	char * valor;
	unsigned short apariciones;
	abb_t* palabras;
};

//Pre: se le pasa el valor de una palabra
//Post: si el valor no es nulo se devuelve una palabra con frecuencia 1
palabra_t* palabra_crear(char* valor){
	if(!valor) return NULL;
		if( strcmp(valor,"") ==0) return NULL;

	palabra_t* nuevapalabra=malloc(sizeof(palabra_t));
	if (!nuevapalabra)
		return NULL;
	nuevapalabra->valor = malloc(sizeof(char)*(strlen(valor)+1));
	strcpy(nuevapalabra->valor,valor);
	nuevapalabra->frecuencia = 1;
	return nuevapalabra;
}

//Pre: se le pasa una palabra por parametro
//Post: destruye la palabra
void palabra_destruir(void* unapalabra){

	if(!unapalabra)return;
	free(((palabra_t*)unapalabra)->valor);
	free(unapalabra);
	return;
}

//Pre: se le pasa una palabra por parametro
//Post: si la palabra es valida devuelve su frecuencia y 0 en los demas casos.
unsigned short obtener_frecuencia(palabra_t* unapalabra){
	if (!unapalabra)
		return 0;
	return unapalabra->frecuencia; 
}

//Pre: se le pasa una palabra por parametro
//Post: si la palabra es valida suma 1 a su frecuencia devuelve un bool 
//informando el exito de la operacion.
bool sumar_frecuencia(palabra_t* unapalabra){
	if (!unapalabra)
		return false;
	unapalabra->frecuencia++;
	return true; 	
}

//Pre: se le pasa una palabra por parametro
//Post: si la palabra es valida resta 1 a su frecuencia devuelve un bool 
//informando el exito de la operacion.
bool restar_frecuencia(palabra_t* unapalabra,size_t valor){
	if (!unapalabra)
		return false;
	if ( unapalabra->frecuencia == 0 )
		return false;

	unapalabra->frecuencia =unapalabra->frecuencia-valor;
	if	(unapalabra->frecuencia<0)
		unapalabra->frecuencia = 0;
		
	return true; 	
}

//Pre: se le pasa un valor no nulo y no vacio
//Post: devuelve un puntero al contexto nuevo si se cumplieron las precondiciones o null en otro caso.
contexto_t* ctx_crear(char* valor){
	if (!valor)
		return NULL;
	if (strcmp(valor,"") == 0) 
		return NULL;
	contexto_t* nuevoContexto = malloc (sizeof(contexto_t));
	if (!nuevoContexto)
		return NULL;
	nuevoContexto->palabras = abb_crear(strcmp,palabra_destruir);
	if (!nuevoContexto->palabras){
		//si no pudo inicializar la lista
		free(nuevoContexto);
		return NULL;
	}
	nuevoContexto->valor = malloc(sizeof(char)*(strlen(valor)+1));
	if (!nuevoContexto->valor){
		free(nuevoContexto->palabras);
		free(nuevoContexto);
		return NULL;
		
	}
	nuevoContexto->apariciones=0;
	strcpy(nuevoContexto->valor,valor);
	return nuevoContexto;
	
}
//Pre:se le pasa un contexto 
//Post: si se cumplen las precondiciones devuelve la frecuencia de aparicion
//de ese contexto, devuelve 0 en caso contrario
unsigned short ctx_obtener_apariciones(contexto_t* unContexto){
	if (!unContexto)
		return 0;
	return unContexto->apariciones;
}
//Pre:se le pasa un contexto valido y una palabra <> de ""
//Post: Creo la nueva palabra en el conexto sino existia, actializo su frecuencia si ya existia.
//devuelve true si se cumplieron las precondiciones, false en otro caso
bool ctx_ingresar_palabra(contexto_t *unContexto,char* unaPalabra){
	if ( (!unaPalabra) || (!unContexto) )
		return false;
	if (strcmp(unaPalabra,"") == 0) 
		return false;
		
	//veo si la palabra pertenece a palabras
	if ( abb_pertenece(unContexto->palabras,unaPalabra) ){
			//encontre la palabra
			palabra_t* palabraEnContexto = abb_obtener(unContexto->palabras,unaPalabra);
			sumar_frecuencia(palabraEnContexto);
	}else
	{
		//la palabra no existia
		palabra_t* nuevaPalabra = palabra_crear(unaPalabra);
		if (!nuevaPalabra)
			return false;
		bool control= abb_guardar(unContexto->palabras,nuevaPalabra->valor,nuevaPalabra);
		if (!control)
			return false;
	}
	unContexto->apariciones++;
	return true;
}
//Pre:se le pasa un contexto valido y una palabra <> de ""
//Post: Creo la nueva palabra en el conexto sino existia, actializo su frecuencia si ya existia.
//devuelve true si se cumplieron las precondiciones, false en otro caso
bool ctx_borrar_frecuencia(contexto_t *unContexto,char* unaPalabra,unsigned short valor){
	if ( (!unaPalabra) || (!unContexto))
		return false;
	if (strcmp(unaPalabra,"") == 0) 
		return false;
		
	//veo si la palabra pertenece a palabras
	bool encontrado =  abb_pertenece(unContexto->palabras,unaPalabra);
	if (encontrado){
		//encontre la palabra
		palabra_t* palabraEnContexto = abb_obtener(unContexto->palabras,unaPalabra);
		restar_frecuencia(palabraEnContexto,valor);
		//si quedo con frecuencia 0 lo borro
		if ( obtener_frecuencia(palabraEnContexto) == 0)			
			abb_borrar(unContexto->palabras,unaPalabra);
	}
	return encontrado;
}

//Pre:se le pasa un contexto y una palabra como parametros
//Post: si se cumplen las precondiciones devuelve la frecuencia de aparicion
//de esa palabra en ese contexto, devuelve 0 en caso contrario
unsigned short ctx_obtener_frecuencia(contexto_t* unContexto,char* unaPalabra){
	if ( (!unaPalabra) || (!unContexto) )
		return 0;
	if (strcmp(unaPalabra,"") == 0)
		return 0;
		
	//veo si la palabra pertenece a palabras
	if ( abb_pertenece(unContexto->palabras,unaPalabra) ){
		//encontre la palabra
		palabra_t* palabraEnContexto = abb_obtener(unContexto->palabras,unaPalabra);	
		return obtener_frecuencia(palabraEnContexto);
	}
	return 0;
	
}
//Pre:se le pasa un contexto 
//Post: si se cumplen las precondiciones devuelve un char* con el valor de 
//de ese contexto devuelve NULL en caso contrario
char* ctx_obtener_valor(contexto_t* unContexto){
	if (!unContexto)
		return NULL;
	return unContexto->valor;
}
//Pre: se le pasa un contexto por parametro
//Post: destruye el contexto
void ctx_destruir(void *unContexto){
	if(!unContexto)return;
	abb_destruir(((contexto_t*)unContexto)->palabras);
	free( ((contexto_t*)unContexto)->valor);
	free(unContexto);
	return;
}
//Pre: 
//Post: devuelve un puntero al contenedor de contexto nuevo si se no hubo problemas o null en otro caso.
ctdctx_t* ctdctx_crear(){
	
	ctdctx_t* nuevoContenedor=malloc(sizeof(ctdctx_t));
	if (!nuevoContenedor)
		return NULL;
	nuevoContenedor->cantidad =0;
	nuevoContenedor->contextos = abb_crear(strcmp,ctx_destruir);
	if (!nuevoContenedor->contextos){
		free(nuevoContenedor);
		return NULL;
	}
	return nuevoContenedor;
}

//Pre:se le pasa una palabra contexto y una palabra a ingresar en el contexto
//Post:  si el contexto no exite devuelve false, si el contexto existe y la palabra ingresada es correcta devuelve true
//devuelve false en cualquier otro caso.
bool ctdctx_ingresar_palabra(ctdctx_t* contenedor,char *unContexto,char* unaPalabra){
	if ( (!unContexto) || (!unaPalabra) || (!contenedor))
		return false;
	if ( ( strcmp(unContexto,"") ==0 ) || ( strcmp(unaPalabra,"") ==0 ) )
		return false;
	if(!abb_pertenece(contenedor->contextos,unContexto)){
		//es un contexto nuevo
		contexto_t* nuevoContexto = ctx_crear(unContexto);
		if (!nuevoContexto)
			return false;
		bool control = ctx_ingresar_palabra(nuevoContexto,unaPalabra);
		if (!control)
			return false;//si hubo error
		control=abb_guardar(contenedor->contextos,unContexto,nuevoContexto);
		if (!control){
			ctx_destruir(nuevoContexto);
			return false;//si hubo error
		}
		contenedor->cantidad = contenedor->cantidad +2;
	}else{
		contexto_t* contextoViejo = abb_obtener(contenedor->contextos,unContexto);
		bool control = ctx_ingresar_palabra(contextoViejo,unaPalabra);
		if (!control)
			return false;//si hubo error
			contenedor->cantidad++;
	}
		
	return true;
}

//Pre:se le pasa un contenedor
//Post:  si el contenedor no exite o esta vacio devuelve 0, devuelve
//la cantidad de palabras en otro caso
unsigned short ctdctx_cantidad(ctdctx_t* unContenedor){
	if (!unContenedor)
		return 0;
	return unContenedor->cantidad;
}


//Pre: se le pasa un contenedor de contextos por parametro
//Post: destruye el contexto
void ctdctx_destruir(ctdctx_t *unContenedor){
	if (!unContenedor)
		return;
	abb_destruir(unContenedor->contextos);
	free(unContenedor);
}


